#include <gmock/gmock.h>
#include <gtest/gtest.h>

class File {
public:
    virtual ~File() = default;
    virtual int open(const char* name) = 0;
    virtual int close() = 0;
    virtual int read(char* buf, size_t size) = 0;
    virtual int write(const char* buf, size_t size) = 0;
};

class Logger {
public:
    explicit Logger(File* f) : file_(f) {}

    bool init() const { return file_->open("log.txt") == 0; }

    bool write(const char* buf, const size_t size) const {
        return file_->write(buf, size) == size;
    }

private:
    File* file_;
};

class MockFile : public File {
public:
    MOCK_METHOD(int, open, (const char* name), (override));
    MOCK_METHOD(int, close, (), (override));
    MOCK_METHOD(int, read, (char* buf, size_t size), (override));
    MOCK_METHOD(int, write, (const char* buf, size_t size), (override));
};

TEST(GMockDemo, InitFailed) {
    MockFile mockFile;
    EXPECT_CALL(mockFile, open(testing::_)).WillOnce(testing::Return(-1));

    const Logger logger(&mockFile);
    EXPECT_FALSE(logger.init());
}

TEST(GMockDemo, WriteFailed) {
    MockFile mockFile;
    EXPECT_CALL(mockFile, open(testing::_)).WillOnce(testing::Return(0));

    EXPECT_CALL(mockFile, write(testing::_, 10))
        .Times(3)
        .WillOnce(testing::Return(10))
        .WillOnce(testing::Return(-1))
        .WillOnce(testing::Return(5));

    const Logger logger(&mockFile);
    EXPECT_TRUE(logger.init());
    EXPECT_TRUE(logger.write(nullptr, 10));
    EXPECT_FALSE(logger.write(nullptr, 10));
    EXPECT_FALSE(logger.write(nullptr, 10));
}